#ifndef SCENE_CU
#define SCENE_CU

#include "../Scene.hpp" 
// #include "Vector_dev.hpp"
// #include "../Ray.hpp"
// #include "../Material.hpp"
#include "../BVH.hpp"
#include "../Triangle.hpp"

void Scene::buildBVH(){
    this->bvh = new BVHAccel(objects, objects_num , 1, BVHAccel::SplitMethod::NAIVE);
}

__device__ void Scene::sampleLight(Intersection &pos, float &pdf,int thread_id) const
{
    float emit_area_sum = 0.F;
    for (uint32_t k = 0; k < objects_num; ++k)
    {
        if (((MeshTriangle *)objects[k])->hasEmit())
        {
            emit_area_sum += ((MeshTriangle *)objects[k])->getArea();
        }
    }
    float p = get_random_float(thread_id) * emit_area_sum;
    emit_area_sum = 0;
    for (uint32_t k = 0; k < objects_num; ++k)
    {
        if (((MeshTriangle *)objects[k])->hasEmit())
        {
            emit_area_sum += ((MeshTriangle *)objects[k])->getArea();
            if (p <= emit_area_sum)
            {
                ((MeshTriangle *)objects[k])->Sample(pos, pdf,thread_id);
                break;
            }
        }
    }
}

__device__ Intersection Scene::intersect(const Ray &ray) const
{
    return this->bvh->Intersect(ray);
}

__device__ Vector3fDev Scene::castRay(const Ray &ray, int depth,int thread_id) const
{
    // TO DO Implement Path Tracing Algorithm here
    Intersection shade_inter = Scene::intersect(ray);
    if (shade_inter.happened)
    {

        Vector3fDev hitColor = shade_inter.m->getEmission();

        Vector3fDev p = shade_inter.coords;
        Vector3fDev wo = ray.direction;
        Vector3fDev N = shade_inter.normal;

        Intersection sample_inter;
        float pdf_light;
        sampleLight(sample_inter, pdf_light,thread_id);

        Vector3fDev x = sample_inter.coords;
        Vector3fDev ws = normalize(x - p);

        float distance_pTox = (x - p).norm();
        if (dotProduct(wo, N) >= 0)
            p = p - N * EPSILON;
        else
        {
            p = p + N * EPSILON;
        }

        Ray ray_pTox(&p, ws);
        Intersection block_inter = Scene::intersect(ray_pTox);
        // if (block_inter.happened && block_inter.m->hasEmission())
        if (block_inter.happened && (distance_pTox - block_inter.distance) < 0.01)
        {
            Vector3fDev NN = -block_inter.normal;
            Vector3fDev emit = block_inter.m->m_emission;
            Vector3fDev L_dir = emit * block_inter.m->eval(wo, ws, N) * dotProduct(ws, N) * dotProduct(ws, NN) / (distance_pTox * distance_pTox) / pdf_light;
            hitColor = hitColor+ L_dir;
        }

        if (get_random_float(thread_id) < RussianRoulette)
        {
            // wi=sample(wo,N)
            Vector3fDev wi = normalize(shade_inter.m->sample(wo, N,thread_id));
            // Trace a ray r(p,wi)
            Ray ray_pTowi(&p, wi);
            // If ray r hit a non-emitting object at q
            Intersection bounce_point_inter = Scene::intersect(ray_pTowi);
            // if (bounce_point_inter.happened && !bounce_point_inter.m->hasEmission())

            if (bounce_point_inter.happened ){
                if(bounce_point_inter.obj->subClassType==1 && !((MeshTriangle *)bounce_point_inter.obj)->hasEmit())
                {
                    Vector3fDev L_indir = castRay(ray_pTowi, depth + 1,thread_id) * shade_inter.m->eval(wo, wi, N) * dotProduct(wi, N) / (shade_inter.m->pdf(wo, wi, N) * RussianRoulette);
                    hitColor = hitColor+ L_indir;
                }
                else if(bounce_point_inter.obj->subClassType==2 && !((Triangle *)bounce_point_inter.obj)->hasEmit())
                {
                        Vector3fDev L_indir = castRay(ray_pTowi, depth + 1,thread_id) * shade_inter.m->eval(wo, wi, N) * dotProduct(wi, N) / (shade_inter.m->pdf(wo, wi, N) * RussianRoulette);
                        hitColor = hitColor+ L_indir;
                }
                else if(bounce_point_inter.obj->subClassType!=1 && bounce_point_inter.obj->subClassType!=2)
                {
                    printf("UnImplement Object's subclass\n");
                    printf("----------------------ERROR RESULT----------------------\n");
                }
            }
        }
        return hitColor;
    }
    else
    {
        return Vector3fDev(0.F, 0.F, 0.F);
    }
    return Vector3fDev(0.F, 0.F, 0.F);
}

#endif